import os
import time
import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from measurement.preprocess_probe_data import *
from pylab import mpl
from utils.mylib_db import *
from utils.mylib_utils import *
from measurement.measurement_utils import *
from utils.gum_algorithm23.draw_fig import *
import multiprocessing

"""
？？？？

"""

db_table_function_fitting = "bt_data_analysis_function_fitting"
db_data_head_table = "bt_data_head"

MAX_LEN = 1000     # 每次插入数据库的最大数据条数（批量插入功能）

NoneStr = 'NoneNone'        # 常量（区别于数据库默认填充的None值）
NoneValue = -999999.99        # 常量（表示None值）

# 评价指标计算结果图保存路径（固定不变），包括3张图：平均偏差、标准偏差、相关系数
draw_figure_save_path = "media/data/figure_compare_index_calculation"
# db_table_draw_figure = "bt_data_figure"


def dataCompare(param):
    """
    数据分析接口，包括：
        1. 时间统一分辨率
        2. 高度统一分辨率
        3. 比对值？？
    """
    print("进入数据比对过程..")
    print("参数情况：")
    print(param)

    # 参数处理
    task_id = param.get('task_id') if param.get('task_id') is not None else get_current_timestamp()                     # TaskId默认为当前时间

    # 筛选条件（数据查询参数）
    data_source = int(param.get('data_source')) if param.get('data_source') is not None else 0                          # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据
    station_device_pair_list = param.get('station_device_pair_list') if param.get('station_device_pair_list') is not None else ""       # 站点设备对字符串，格式"1-2", 或者 "1-2,1-3,2-4" (某些模块传多组参数)
    station_id = int(param.get('station_id')) if param.get('station_id') is not None else 0                             # 站点编号
    device_id = int(param.get('device_id')) if param.get('device_id') is not None else 0                                # 设备编号
    # value_type_str = param.get('value_type') if param.get('value_type') is not None else ""                             # 参量类型，格式："0" 或 "0,1,2,3"，其中0:温度；1:密度；2:纬向风速; 3:径向风速；（2309调整：改成内部转换，不接受界面输入）
    data_type = param.get('data_type') if param.get('data_type') is not None else ""                                    # 数据类型，格式"TSVP" 或 "TSVP, DUSM"等（2309调整为：T/P/W1/W2/D/N/F）
    time_start = param.get('time_start') if param.get('time_start') is not None else "0001-01-01 00:00:00"              # 时间范围的起始时间，格式：YYYY-mm-dd HH:MM:SS
    time_end = param.get('time_end') if param.get('time_end') is not None else "0001-01-01 00:00:00"                    # 时间范围的结束时间，格式：YYYY-mm-dd HH:MM:SS
    height_start = param.get('height_start') if param.get('height_start') is not None else ""                           # 高度区间起始值（暂时不使用）
    height_end = param.get('height_end') if param.get('height_end') is not None else ""                                 # 高度区间结束值（暂时不使用）
    # 用于同设备不同时间比较（第二个时间区间）
    time_start2 = param.get('time_start_2') if param.get('time_start_2') is not None else "0001-01-01 00:00:00"         # 时间范围的起始时间，格式：YYYY-mm-dd HH:MM:SS
    time_end2 = param.get('time_end_2') if param.get('time_end_2') is not None else "0001-01-01 00:00:00"               # 时间范围的结束时间，格式：YYYY-mm-dd HH:MM:SS

    # 文件导入路径（data_source=2时生效）
    import_file_path = param.get('import_file_path') if param.get('import_file_path') is not None else ""               # 文件导入支持参数：单个文件路径，多个文件路径，单个目录，格式"path1" 或 "path1;path2;path3"或"directory"（当data_source选择了2文件导入后，该字段必填）
    process_num = int(param.get('process_num')) if param.get('process_num') is not None else DEFAULT_PROCESS_NUM        # 默认4个进程同时读入文件
    user_id = int(param.get('user_id')) if param.get('user_id') is not None else -1                                     # 当前登录用户

    # 指定数据文件
    head_id_list = param.get('head_id_list') if param.get('head_id_list') is not None else ""                           # 默认值为：""，格式为 2,3,4,5（暂不传参）
    data_level = int(param.get('data_level')) if param.get('data_level') is not None else 1                             # 默认为1，0不选；1En；2Z比分数

    # 比对方法
    compare_type = int(param.get('compare_type')) if param.get('compare_type') is not None else 0                       # 0不同设备比对；1同设备不同时间比对
    compare_method = int(param.get('compare_method')) if param.get('compare_method') is not None else 0                 # 默认为1，比对结果评价方法：0不选；1En；2Z比分数

    # 202402添加新功能：（补充性功能：如果点击了 评价指标计算，在正常的比对基础上，同时执行 评价指标计算）
    # 数据比对--“观测数据-观测数据比对”子模块增加功能：添加“评价指标计算”按钮，结果展示中增加三个tab显示：平均偏差、标准偏差、相关系数。
    # 画图输入参数为：两条廓线：高度、探测值、参量类型。实现方案：Python画图，提供链接给前台界面展示。
    check_index_calculation = int(param.get('check_index_calculation')) if param.get('check_index_calculation') is not None else 0   # 0不进行评价指标计算；1评价指标计算


    # 比对标准设置
    compare_standard = int(param.get('compare_standard')) if param.get('compare_standard') is not None else 1           # 设置比对标准：0:和标准廓线对比；1:设置比较基准（和第一个站点的第一个设备的数据比对）

    # 高度统一分辨率参数（界面不支持选择，直接使用默认值）
    statistic_method = param.get('statistic_method') if param.get('statistic_method') is not None else "0"              # 统计处理方法：0平均值计算；1标准差计算；2最大值计算；3最小值计算，可多选，形成字符串参数，如"0,1,2,3" 或 "0"
    imputation_method = param.get('imputation_method') if param.get('imputation_method') is not None else "1"           # 缺失值填充方法：0:不填充；1:线性插值、2:对数插值、3:三次样条插值、4:均值填充、5:中位数填充、6:最小二乘法填充、7:切比雪夫拟合填充.可多选，形成字符串参数

    # 以下四个参数数据预处理同数据分析界面功能
    height_resolution = int(param.get('height_reslution')) if param.get('height_reslution') is not None else 1          # 统一高度分辨率，数据为高度间隔值
    height_resolution_unit = int(param.get('height_reslution_unit')) if param.get('height_reslution_unit') is not None else 0    # 统一高度分辨率单位，0:km(默认),1:m
    time_resolution = int(param.get('time_reslution')) if param.get('time_reslution') is not None else -1               # 统一时间分辨率，数据为时间间隔值1
    time_resolution_unit = int(param.get('time_reslution_unit')) if param.get('time_reslution_unit') is not None else 0 # 统一时间分辨率单位，0:小时/1:日/2:周/3:月/4:季度/5:年/6:全时段区间等

    # 判断参数合理性
    # if value_type_str == "":
    #     print("参数错误：参量类型不能为空！")
    #     return {"code": -1, "msg": "参数错误：参量类型不能为空！"}

    # 新老探测体制临时转换
    # data_type = getMultiDataTypeStr2309(data_type)
    value_type_str = getMultiValutTypeStrByDataType2309(data_type)

    conf = {
        'TaskId': task_id,
        'Type': 2,          # 参数表类型字段标识
        'HeightReslution': height_resolution,
        'HeightReslutionStep': 1,
        'HeightReslutionUnit': height_resolution_unit,
        'TimeReslution': time_resolution,
        'TimeReslutionStep': 1,
        'TimeReslutionUnit': time_resolution_unit,
        'TimeReslutionUnitOption': ['小时', '日', '周', '月', '季度', '年', '全时段'],
        'IsTimeProcessed': 0,  # 标记是否进行了时间分辨率统一化处理

        "StatisticMethod": statistic_method,        # 默认"0" （数据比对界面不支持选择，直接使用默认值）
        'ImputationMethod': imputation_method,      # 默认"1" （数据比对界面不支持选择，直接使用默认值）
        # 'HeightStandardSet': height_resolution_unit,
        # 'TimeStandardSet': time_resolution_unit,

        "CompareMethodList": ["", "En值", "Z比分数"],
        "CompareMethod": compare_method,
        "CompareType": compare_type,                    # 0不同设备比对；1同设备不同时间比对
        "CompareStandard": compare_standard,            # 0:和标准廓线比对，1:和用户选择的第一条数据廓线比对
        "TaskType": 8 if compare_type == 0 else 9,      # 8:数据比对不同设备；9:数据比对不同时间

        "CheckIndexCalculation": check_index_calculation,
        # "CheckIndexCalculation": 1,     # 测试
        'FigureSavePath': draw_figure_save_path,

        # 数据源
        'DataSource': data_source,
        # 支持指定headIdList
        'HeadIdList': head_id_list,
        'StationDevicePairList': station_device_pair_list.split(',') if station_device_pair_list != "" else [],
        'StationId': station_id,
        'DeviceId': device_id,
        'ValueType': value_type_str,
        'DataType': data_type,
        'TimeStart': "0001-01-01 00:00:00" if time_start=="" else time_start,
        'TimeEnd': "0001-01-01 00:00:00" if time_end=="" else time_end,
        'HeightStart': height_start,
        'HeightEnd': height_end,
        'TimeStart2': "0001-01-01 00:00:00" if time_start2=="" else time_start2,
        'TimeEnd2': "0001-01-01 00:00:00" if time_end2=="" else time_end2,

        'SaveFlag': 0,              # 数据比对的结果都不存库

        'DataLevel': data_level,

        # 用户文件导入相关
        'ImportFilePath': import_file_path,
        "ProcessNum": process_num,
        "UserId": user_id,

        'DbHeadTable': "",          # 数据筛选源Head表
        'DbTable': "",              # 数据筛选源Data表
        'DbSaveHeadTable': "",      # 数据存储Head表
        'DbSaveTable': "",          # 数据存储Data表
    }
    print("参数值：")
    print(conf)
    # 参数检查通过
    # 保存参数 TODO
    param_conf_id = -1
    param_conf_id = saveDataCompareParametersDataIntoDB(conf)
    conf["ParameterSetting"] = param_conf_id
    print(param_conf_id)

    # 指定数据筛选库表和存储库表
    # 指定保存数据的库表（预处理数据对象是L2,如果存库，进正式表，如果不存，临时表）
    # 针对其他场景，进行修改（导入文件，存临时表）
    # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据
    # 保存数据
    if conf['SaveFlag'] == 1:
        conf['DbSaveHeadTable'] = db_data_head_table
        conf['DbSaveTable'] = db_raw_l2_precessed_table
    else:
        conf['DbSaveHeadTable'] = db_temporary_data_head_table
        conf['DbSaveTable'] = db_temporary_data_table_l2

    # 获取数据（conf['DataSource'] == 3情况，需根据任务表是否存库字段确定）
    if conf['DataSource'] == 0:
        conf['DbHeadTable'] = db_data_head_table
        conf['DbTable'] = db_raw_l2_precessed_table
    elif conf['DataSource'] == 1:
        conf['DbHeadTable'] = db_data_head_table
        conf['DbTable'] = db_raw_l2_data_table
    elif conf['DataSource'] == 2:
        #  2:文件导入：临时表，结果不存库
        conf['DbHeadTable'] = db_temporary_data_head_table
        conf['DbTable'] = db_temporary_data_table_l2
        conf['DbSaveHeadTable'] = db_temporary_data_head_table
        conf['DbSaveTable'] = db_temporary_data_table_l2
    # elif conf['DataSource'] == 3:  # 查任务表后进行确认数据存储源数据库表
    else:
        conf['DbHeadTable'] = db_temporary_data_head_table
        conf['DbTable'] = db_temporary_data_table_l2

    # 设置任务开始
    TASK_ID = conf['TaskId']
    if TASK_ID != "":
        # 5:一键评定;6:评定结果查询（文件上传）；7:不确定度校验（文件上传）；8:数据比对不同设备；9:数据比对不同时间
        ret = setTaskStatus(TASK_ID, conf['TaskType'], 0, -1, conf['DataSource'])
        if ret != "OK":
            print("setTaskStatus: %s" % ret)
            return ret

    # 对data_head_id_list中的所有数据进行高度分辨率处理和时间分辨率处理
    processes = []
    process = multiprocessing.Process(target=runDataCompareTaskFunc, args=(conf, TASK_ID))      # 正式运行用（捕捉错误，设置任务完成状态）
    # process = multiprocessing.Process(target=runDataCompareTask, args=(conf, TASK_ID))        # 开发调试用，暴露出错误代码行号
    processes.append(process)
    process.start()

    return 0


def runDataCompareTaskFunc(conf, TASK_ID):
    try:
        start_time = time.time()    # 计时开始
        ret = runDataCompareTask(conf, TASK_ID)
        print(ret)
        end_time = time.time()      # 计时结束
        #计算代码运行时间(ms)
        run_time = (end_time - start_time) * 1000
        #print('代码运行时间为{:.2f} s'.format(run_time))
        print('代码运行时间为%d ms' % run_time)

        if ret != 0:
            print("设置任务状态为 完成状态 ！")
            setTaskStatusFinish(conf)

        # 以tkinter窗口形式给出系统错误提示
        if 'code' in ret.keys() and ret['code'] == -1:
            err_msg = ret['msg']
            print("错误：", err_msg)
            show_msg_to_tkinter(err_msg)

    except Exception as e:
        print("runDataCompareTaskFunc_Error: %s" % e)
        setTaskStatusFinish(conf)
        # 以tkinter窗口形式给出系统错误提示
        show_msg_to_tkinter(e)


def runDataCompareTask(conf, TASK_ID):
    ######################################
    # 保存数据
    if conf['DataSource'] == 2:
        print("开始导入用户数据..")
        ret = callReadFileProcessAndWaitFinish(conf, conf['ImportFilePath'], conf['ProcessNum'], TASK_ID)
        print("读取用户文件结束！")
        print("进入筛选文件数据阶段..")

    ######################################
    # 获取数据
    print("开始筛选文件数据阶段..")
    head_id_list = conf['HeadIdList']

    # 按查询条件获取探测数据（head_id_list）
    data_head_id_list = []
    # 按检索的站点-设备分组存放head_id，
    # 1. 不同站点设备比对：{"start_time1":[headid1, headid2,...], "start_time2":[headid1, headid2, headid3], ...}
    # 2. 同站点设备不同时间段比对：
    #       比如：对同一个站点设备，选择两个时间段
    #       时间段1：2022-6-1  - 2022-6-10
    #       时间段2：2022-7-1  - 2022-7-10
    #       方案：使用第一个时间段的第一条数据作为基准，其他所有数据和基准值做差值。
    data_head_id_list_group = {}                  # 见后举例
    data_head_id_group_by_station_device = {}     # 按“站点-设备”的分类存放head_id数据，便于进行时间分辨率处理（见后举例）

    # 查询流程：1）优先检查是否提供了headid，2）其次根据datasource判断是按条件检索或检索最近一次任务的结果
    if head_id_list != "":
        # 如果参数中指定了head_id_list
        print("参数指定了Head_id_list!优先处理..")
        # data_head_id_list = []
        head_id_list_arr = head_id_list.split(",")
        if len(head_id_list_arr) > 0:
            for head_id in head_id_list_arr:
                print(head_id)
                ret = getHeadIdInfoByHeadId(int(head_id))
                if ret != "":
                    data_head_id_list.append(ret)
        else:
            print("参数head_id_list格式错误！")
            return {"code": -1, "msg": "提供的参数head_id_list格式错误！"}

    elif conf['DataSource'] == 0 or conf['DataSource'] == 1:
        # 对数据源 0:数据库预处理数据，1:数据库原始数据 2:文件导入;
        print("根据数据源，按条件筛选head_id_list..")
        if conf['ValueType'] == "":
            print("参量类型ValueType不能为空")
            return {"code": -1, "msg": "当前选择的数据源，参量类型参数不能为空！"}

        # 不同设备比对
        # "1-2,1-3,2-4"
        print(conf)
        if len(conf['StationDevicePairList'])>0:
            # 1:数据比对不同设备；2:数据比对同设备不同时间
            if conf['CompareType'] == 0:
                # 只在数据比对的不同设备情况下，计算En/Z值/Zeta值
                cnt = 0
                # if conf['CompareStandard'] == 1 and len(conf['StationDevicePairList']) > 0:   # （2022-09-12 修改）
                # 标准廓线或设备之间比对，都适用
                if len(conf['StationDevicePairList']) > 0:
                    standard_data_label = conf['StationDevicePairList'][0]     # 用作比对标准的数据集（站点-设备 对）
                # print("标准数据：%s" % standard_data_label)
                for pair in conf['StationDevicePairList']:
                    arr = pair.split('-')
                    conf['StationId'] = int(arr[0])
                    conf['DeviceId'] = int(arr[1])
                    # data_head_id_list_part = queryDataByCondition(conf, 0)
                    data_head_id_list_part = queryDataByCondition2309(conf, 0)  # 针对202309数据分表存储情况查询
                    if len(data_head_id_list_part) > 0:
                        # data_head_id_list.extend(data_head_id_list_part)

                        # 按“站点-设备”的分类存放head_id数据
                        if pair not in data_head_id_group_by_station_device.keys():
                            data_head_id_group_by_station_device[pair] = []

                        # 按设备记录head_id，用于设置比较基准数据情况（ 标准廓线或设备之间比对，都适用，即标准廓线比对时，支持选择多个设备）
                        # if conf['CompareStandard'] == 1:   # （2022-09-12 修改）
                        for item in data_head_id_list_part:
                            # start_time = item[2]
                            start_time = getTimeFormat(item[2], conf["TimeReslutionUnit"])    # 使用时间分辨率组织数据（20240327修改）
                            if start_time not in data_head_id_list_group.keys():
                                data_head_id_list_group[start_time] = []

                            cnt += 1
                            # 规则：不同设备同一时间数据进行比对，同一小时/日/月/季度/年等，如果标准数据中没有当前时间的数据，则抛弃数据，无法进行比对
                            # 若要进行比对，可放大时间分辨率，如同一月/季度/年等
                            if pair == standard_data_label or (pair != standard_data_label and len(data_head_id_list_group[start_time])>0):
                                # data_head_id_list_group[start_time].append(item)
                                data_head_id_list_group[start_time].append(item[0])   # 只保存headID值

                                # 按“站点-设备”的分类存放head_id数据
                                data_head_id_group_by_station_device[pair].append(item[0])

                                # 只处理有标准比对的数据（即第一个站点设备有查询结果的日期）
                                data_head_id_list.append(item)

                        # else:
                        #     # 同标准廓线比较，无法计算En值（因为标准廓线没有GUM值）
                        #     data_head_id_list.extend(data_head_id_list_part)
                print("数据结果，比对数据量：%d （总检索数据量：%d）" % (len(data_head_id_list), cnt))
                print("数据结果，比对时间区间量：%d" % len(data_head_id_list_group))
                print("data_head_id_list:", data_head_id_list)
                print("data_head_id_list_group:", data_head_id_list_group)

            elif conf['CompareType'] == 1:
                arr = conf['StationDevicePairList'][0].split('-')
                conf['StationId'] = int(arr[0])
                conf['DeviceId'] = int(arr[1])

                if conf['TimeStart2'] != "" and conf['TimeEnd2'] != '':
                    # data_head_id_list = queryDataByCondition(conf, 0)
                    data_head_id_list = queryDataByCondition2309(conf, 0)  # 针对202309数据分表存储情况查询
                    print(data_head_id_list)
                    conf['TimeStart'] = conf['TimeStart2']
                    conf['TimeEnd'] = conf['TimeEnd2']
                    # data_head_id_list_part = queryDataByCondition(conf, 0)
                    data_head_id_list_part = queryDataByCondition2309(conf, 0)  # 针对202309数据分表存储情况查询
                    if len(data_head_id_list_part) > 0:
                        data_head_id_list.extend(data_head_id_list_part)
                else:
                    print("比对类型参数错误：同一个设备不同时间段比对的时间设置有误！")
                    return {"code": -1, "msg": "比对类型参数错误：同一个设备不同时间段比对的时间设置有误"}
            else:
                print("比对类型参数错误：只允许不同设备同时间段和同一个设备不同时间段比对！")
                return {"code": -1, "msg": "比对类型参数错误：只允许不同设备同时间段和同一个设备不同时间段比对"}
        else:
            # data_head_id_list = queryDataByCondition(conf, 0)
            data_head_id_list = queryDataByCondition2309(conf, 0)  # 针对202309数据分表存储情况查询

        if len(data_head_id_list) == 0:
            print("错误：未检索到探测数据，请重新设置检索条件！")
            return {"code": -1, "msg": "错误：未检索到探测数据，请重新设置检索条件！"}

    elif conf['DataSource'] == 3:
        # 对数据源 3:最新处理的数据。获取最近一次的录入数据，对此进行数据预处理
        # 根据任务确定数据库表筛选数据情况
        print("取最新一次任务处理结束的数据..")
        data_batch_info = getLatestDataBatchId(0)
        if data_batch_info == "":
            print("错误：未找到最近录入的数据")
            return {"code": -1, "msg": "未找到最近录入的数据！"}
        data_batch_id = data_batch_info['KeyName']
        print("data_batch_id: %s" % data_batch_id)

        # 确定数据录入的数据存储数据库源表（TODO：校验？）
        if data_batch_info['IsSaveIntoDb'] == 1:
            conf['DbHeadTable'] = db_data_head_table
            conf['DbTable'] = db_raw_l2_data_table
        elif data_batch_info['IsSaveIntoDb'] == 0:
            conf['DbHeadTable'] = db_temporary_data_head_table
            conf['DbTable'] = db_temporary_data_table_l2

        data_head_id_list = getHeadIdByBatchId(data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}

    elif conf['DataSource'] == 2:
        # 对数据源 3:最新处理的数据。获取最近一次的录入数据，对此进行数据预处理
        print("获取刚刚导入的用户文件数据..")
        data_batch_id = TASK_ID
        print("data_batch_id: %s" % data_batch_id)

        data_head_id_list = getHeadIdByBatchIdV3(conf, data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}

    else:
        print("参数错误！")
        return {"code": -1, "msg": "参数错误，请检查传入的参数！"}

    print("data_head_id_list: ")
    # print(data_head_id_list)
    print("检索到HearID数量：%d" % len(data_head_id_list))
    if len(data_head_id_list) == 0:
        print("未获取到探测数据！")
        return {"code": -1, "msg": "未获取到探测数据"}

    time.sleep(2)
    # return 0    # 测试

    # 清除临时表数据，参数和setTaskStatus中任务类型一致
    # clear_db_temporary_table_and_keep_last_same_type_task(conf['TaskType'])

    # 设置任务开始
    ret = taskDataCompare(data_head_id_list, conf, conf['DataLevel'], TASK_ID, data_head_id_list_group, data_head_id_group_by_station_device)
    print("比对操作返回结果：", ret)
    # 测试

    # 设置任务结束
    print("将要设置任务结束..")
    if TASK_ID != "":
        print("设置任务结束！")
        updateTaskStatus(TASK_ID, conf['TaskType'])

    # return {"code": 0, "msg": ""}
    return ret


def taskDataCompare(data_head_id_list, conf, data_level, TASK_ID, data_head_id_list_group, data_head_id_group_by_station_device):
    print("数据比对对象：")
    print(data_head_id_list)
    print(conf)
    print("data_head_id_list_group: ", data_head_id_list_group)    # 格式如下：（按小时存放所有数据）
    # {datetime.datetime(2023, 8, 29, 12, 0): [281], datetime.datetime(2023, 8, 29, 13, 0): [504], datetime.datetime(2023, 8, 29, 14, 0): [129, 137, 267, 398, 608, 786, 7], datetime.datetime(2023, 8, 29, 15, 0): [82, 225, 302, 492, 799], datetime.datetime(2023, 8, 29, 16, 0): [50, 110, 295, 510, 810], datetime.dat
    # etime(2023, 8, 29, 17, 0): [127, 166, 521, 820], datetime.datetime(2023, 8, 29, 18, 0): [35, 155, 343, 392, 553, 795], datetime.datetime(2023, 8, 29, 19, 0): [73, 189, 523, 592, 787], datetime.datetime(2023, 8, 29, 20, 0): [61, 226, 824], datetime.datetime(2023, 8, 29, 21, 0): [96, 177, 512, 781]}
    print("data_head_id_group_by_station_device: ", data_head_id_group_by_station_device)    # 格式如下：（按设备存放数据对）
    # {'1-1': [281, 504, 129, 137, 267, 398, 608, 786, 82, 225, 302, 492, 799, 50, 110, 295, 510, 810, 127, 166, 521, 820, 35, 155, 343, 392, 553, 795, 73, 189, 523, 592, 787, 61, 226, 824, 96, 177, 512, 781], '1-2': [7]}
    # return 0    # 测试

    data_list_json = {}    # 缓存原始和预处理中间数据，格式：{"head_id1":[], "head_id2":[], "head_id3":[], }
    height_standard_list = []
    height_standard = set()

    # 预处理参数标记（用户简化参数描述，存入字段）
    # 执行了哪一步，将对应的变量值保存到数据库
    s1_desc_height_resolution = "比对条件：统一高度分辨率：%d km" % conf['HeightReslution']
    s2_desc_time_resolution = "比对条件：统一时间分辨率：%d %s" % (conf['TimeReslution'], conf['TimeReslutionUnitOption'][conf['TimeReslutionUnit']])
    s3_desc_data_compare = "比对方法：%s" % conf['CompareMethodList'][conf['CompareMethod']]
    # 记录上述3步过程执行了哪一步
    pre_process_parameter_list = []

    # [总]获取所有预处理数据，保存到data_list_json
    # 根据head_id，测量数据类型value_type，获取原始数据
    # 同时记录所有高度值（最全的高度）
    # for (head_id, value_type, start_time, end_time) in data_head_id_list:
    for (head_id, value_type, start_time, end_time, station_id, device_id, file_name, file_store_path) in data_head_id_list:
        print("==================================")
        print("head_id = %d, value_type = %s" % (head_id, value_type))
        # raw_data = getRawDataByHeadIdInJsonObjectList(conf, head_id, data_level)        # data_type指定从L0或L2原始数据表中获取数据

        # 获取L2文件路径，读取文件中探测数据
        raw_data = parsingRadarProbeDataFromRawFile2309(file_store_path, file_name, head_id, TASK_ID, 1)

        if raw_data == "":
            print("错误：未找到评定后的数据")
            continue

        print("raw_data:数据条数: %d" % len(raw_data))
        # print(raw_data)

        data_list_json[head_id] = raw_data

        # 获取所有高度
        for data in raw_data:
            # print(data)
            height_standard.add(float(data['Height']))

    if len(data_list_json.keys()) == 0:
        print('未找到探测数据及评定数据！')
        # return -1
        return {"code": -1, "msg": "未找到探测数据及评定数据！"}

    # 所有高度排序
    print(height_standard)
    height_standard_list = list(height_standard)
    height_standard_list.sort()
    print("所有高度：")
    print(height_standard_list)
    if len(height_standard_list) == 0:
        print("未获取到探测高度！")
        # return -1
        return {"code": -1, "msg": "未获取到探测高度！"}
    conf['HeightStandardList'] = height_standard_list

    # 高度分辨率处理是时间分辨率处理和函数拟合的前提，所以在数据获取的地方进行处理
    # 根据需要，进行高度分辨率处理 TODO
    if conf['ImputationMethod'] != "":
        print("【数据分析Step1-高度分辨率统一化】：对多个L2文件进行高度统一化处理..")
        data_list_json_tmp = {}

        # 高度统一化策略1（heigh_unity_method = 0）：所有廓线高度取并集，缺失的高度用最后一个边缘高度值填充
        # 高度统一化策略2（heigh_unity_method = 1）：所有廓线高度取交集，多余的高度去掉
        heigh_unity_method = 1
        if heigh_unity_method == 0:
            max_height_global = 0.0
            min_height_global = 100000.0            # 经验值
        else:
            max_height_global = 100000.0            # 经验值
            min_height_global = 0.0
        # 实现不同高度分辨率
        for head_id in data_list_json.keys():
            raw_data = data_list_json[head_id]
            raw_data = heightNormalizationByStep(raw_data, conf['HeightReslution'])
            if len(raw_data) == 0:
                continue
            print("高度统一化后的数据：%d" % len(raw_data))

            # 差值处理（默认线性差值）
            raw_data = differenceAlgorithm(raw_data, height_standard_list)

            data_list_json_tmp[head_id] = raw_data

            # # 获取最大高度和最小高度
            min_height, max_height = getMaxHeightAndMinHeight(raw_data)
            print("最小最大高度：", head_id, min_height, max_height)
            if heigh_unity_method == 0:
                # 高度统一化策略1：所有廓线高度取并集，缺失的高度用最后一个边缘高度值填充
                min_height_global, max_height_global = getGlobalMaxHeightAndMinHeight(min_height_global, max_height_global, min_height, max_height)
            else:
                # 高度统一化策略2：所有廓线高度取交集，多于的高度去掉
                min_height_global, max_height_global = getGlobalMaxHeightAndMinHeightByIntersection(min_height_global, max_height_global, min_height, max_height)
                if min_height_global == 0 and max_height_global == 0:
                    return {"code": -1, "msg": "错误：所选廓线高度无交集，无法进行数据比对操作！（高度统一化错误）"}

        # 如果raw_data中缺失了高度区间[min_height_global, max_height_global]（步长为step）的某个高度，使用none值填充
        print("高度交集/并集结果：", min_height_global, max_height_global)
        data_list_json_tmp = fillHeightWithNoneValue(data_list_json_tmp, min_height_global, max_height_global, conf['HeightReslution'])

        data_list_json = data_list_json_tmp
        print("高度统一分辨率处理完成！数据head_id个数：%d" % len(data_list_json))
        # print(data_list_json)
        # 记录参数
        pre_process_parameter_list.append(s1_desc_height_resolution)

        time.sleep(2)

    # 注意：时间分辨率处理函数只能支持一个站点的时间分辨率处理！多个站点（相同时间）的数据只能处理第一个站点的数据！
    # data_list_json_normalized = []
    # is_time_processed = 0
    data_head_id_group_by_station_device_new = {}     # 存放head_id时间统一分辨率后，存到temporary_data_head表中的head_id，供后面评价指标分析计算组织输入数据
    if conf['StatisticMethod'] != "" and conf['TimeReslution'] != -1:
        print("【数据分析Step2-时间分辨率统一化】：对多个L2文件进行时间统一化处理..")
        #if conf['TaskType'] == 8 and conf['CompareStandard'] == 1:

        # 多设备数据比对，且数据源不是 本地文件导入的条件下，支持多组数据的时间分辨率统一化处理
        if conf['TaskType'] == 8 and (conf['DataSource'] == 0 or conf['DataSource'] == 1):
            # # 对单个“站点-设备”对进行时间分辨率统一化处理
            # # 时间分辨率处理中，已经将head info存入到对应的head表中（包括新生成的head）
            # print(data_list_json)
            # print(data_list_json.keys())
            # print(data_head_id_group_by_station_device)
            # print(data_head_id_list_group)
            # print("=================")
            # return
            # # 临时
            #
            # # 针对不同的站点设备对，单独进行时间分辨率统一化处理
            data_list_json_tmp = {}
            data_head_id_list_group_tmp = {}
            if len(conf['StationDevicePairList']) > 0:
                standard_data_label = conf['StationDevicePairList'][0]     # 用作比对标准的数据集（站点-设备 对）
            for station_device_pair in data_head_id_group_by_station_device.keys():
                head_id_list = data_head_id_group_by_station_device[station_device_pair]
                head_id_json_tmp_portion = {}
                for head_id in head_id_list:
                    head_id_json_tmp_portion[head_id] = data_list_json[head_id]
                    # 测试
                    # break
                # 时间分辨率处理
                # print("test1: ", head_id_json_tmp_portion)
                head_id_json_tmp_portion = timeNormalization(head_id_json_tmp_portion, conf, TASK_ID)
                data_head_id_group_by_station_device_new[station_device_pair] = list(head_id_json_tmp_portion.keys())
                # print("test2: ", head_id_json_tmp_portion)
                # print("##########################")
                print("head_id_json_tmp_portion: ", head_id_json_tmp_portion.keys())
                for head_id in head_id_json_tmp_portion:
                    data_list_json_tmp[head_id] = head_id_json_tmp_portion[head_id]

                # TODO:需要返回StartTime对应的head_id转换后的新head_id，将data_head_id_list_group进行转换
                # 处理head_id的转换问题
                for head_id in head_id_json_tmp_portion.keys():
                    item = head_id_json_tmp_portion[head_id][0]
                    start_time = item['BinKey']
                    if start_time not in data_head_id_list_group_tmp.keys():
                        data_head_id_list_group_tmp[start_time] = []

                    if station_device_pair == standard_data_label or (station_device_pair != standard_data_label and len(data_head_id_list_group_tmp[start_time])>0):
                        data_head_id_list_group_tmp[start_time].append(item['HeadId'])   # 只保存headID值

                # 测试
                # return

            data_list_json = data_list_json_tmp
            data_head_id_list_group = data_head_id_list_group_tmp
            # print(data_head_id_list_group)

            # print(data_list_json)             # 处理后：head_id变更为新的head_id(来自表：bt_temporary_data_head)
            print("data_head_id_list_group: ", data_head_id_list_group)    # 格式如下：（处理后）
            # {'2023-08-29': [27, 28, 84]}
            # {'2023-08-27': [81, 85], '2023-08-28': [82, 86], '2023-08-29': [27, 28, 84]}
            print("data_head_id_group_by_station_device: ", data_head_id_group_by_station_device)    # 格式如下：（未改变）
            # {'1-1': [281, 504, 129, 137, 267, 398, 608, 786, 82, 225, 302, 492, 799, 50, 110, 295, 510, 810, 127, 166, 521, 820, 35, 155, 343, 392, 553, 795, 73, 189, 523, 592, 787, 61, 226, 824, 96, 177, 512, 781], '1-2': [7]}
            print("data_head_id_group_by_station_device_new: ", data_head_id_group_by_station_device_new)    # 格式如下：（data_head_id_group_by_station_device 映射到新的heads_id后的值,供后面评价指标分析计算组织输入数据）
            # {'1-1': [81, 82, 27], '1-2': [28], '1-4': [85, 86, 84]}
            # return 0   # 测试

        else:
            # 单一headId的时间分辨率处理
            data_list_json = timeNormalization(data_list_json, conf, TASK_ID)
        # is_time_processed = 1
        conf['IsTimeProcessed'] = 1
        # print(data_list_json)

        print("时间统一分辨率处理完毕！数据head_id个数：%d" % len(data_list_json))
        print(data_list_json.keys())

        # 记录参数
        pre_process_parameter_list.append(s2_desc_time_resolution)

        time.sleep(2)

    # 比对方法（计算En/Z/zeta值的过程放入到比对过程中）
    # 只有在不同设备同一时间的情况下，且指定比对基准时，才能计算En/Z/zeta值
    if conf['CompareMethod'] > 0:
        # data_list_json = computeEnZValue(data_list_json, conf, TASK_ID)
        # 记录参数
        pre_process_parameter_list.append(s3_desc_data_compare)

        time.sleep(2)

    # 测试
    # return 0

    if len(pre_process_parameter_list) == 0:
        print("未选择任何功能，退出！")
        # return -1
        return {"code": -1, "msg": "未选择任何功能，退出！"}

    # 处理完毕进行存库
    conf['ParameterDescription'] = ','.join(pre_process_parameter_list)
    # 存库测试

    # 添加标准廓线数据及比对差值 TODO
    # 设置比对标准：0:和标准廓线对比；1:和第一个站点的第一个设备的第1条数据比对？
    if conf['CompareStandard'] == 0:
        height_list = getHeighRange(data_list_json)
        standard_probe_data = getStandardProbeData(height_list)
        data_list_json = compareDataWithStandard(data_list_json, standard_probe_data)
    else:
        if conf['CompareType'] == 0:
            # 不同设备同一时间段比对情况
            print("用户指定标准数据（不同设备同一时间段比对）")
            # print("head表头：")
            # print(data_head_id_list)
            # print("排序后表头")
            # print(data_head_id_list_group)
            # print("数据：")
            # print(data_list_json)
            # print("================================")
            # “评价指标计算”：平均偏差、标准偏差、相关系数。
            # 计算结果画图展示，图片保存在数据库中，前台展示按照任务ID和图片类型筛选。
            if conf['CheckIndexCalculation'] == 1:
                # 判断画“评价指标计算”图参数的合法性：
                if len(data_head_id_group_by_station_device_new) >= 2:
                    retobj = drawIndexCalculationFigure(data_list_json, data_head_id_group_by_station_device_new, conf)
                    if 'code' in retobj.keys() and retobj['code'] == -1:
                        err_msg = retobj['msg']
                        print("错误：", err_msg)
                        return {"code": -1, "msg": err_msg}
                else:
                    print("“评价指标计算”图参数不符合要求：", len(data_head_id_group_by_station_device_new))
                    err_msg = "“评价指标计算”图参数不符合要求！有效廓线数量：%d" % len(data_head_id_group_by_station_device_new)
                    return {"code": -1, "msg": err_msg}
            else:
                print("未设置“评价指标计算”，跳过..")

            # 数据比对
            print("数据比对1data_head_id_list_group：", data_head_id_list_group)
            # print("数据比对2data_list_json：", data_list_json)
            data_list_json = compareDataWithAssignedStandardData(data_list_json, data_head_id_list_group, conf)
            # print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
            # print(data_list_json)
            # return
        else:
            # 同一站点设备不同时间段比对：
            print("用户指定标准数据（同一站点设备不同时间段比对）")
            data_list_json = compareDataWithAssignedStandardDataSameDeviceDiffTime(data_list_json)
            # print(data_list_json)
            # return

    # 特殊处理：如果筛选数据来自正式表，结果存储在临时表
    # 需要把正式表head信息同步插入到临时head表
    if conf['IsTimeProcessed'] == 0 and \
        conf['DbHeadTable'] == db_data_head_table and conf['DbSaveHeadTable'] == db_temporary_data_head_table:
        head_id_pair_list = copyHeadInfoToTempHeadTable(conf, data_list_json.keys())
        print("bt_head表和bt_temp_head表Map：", head_id_pair_list)
        data_list_json = updateDataJsonInfo(head_id_pair_list, data_list_json)

    # 20240508去掉处理结果中的异常值：
    # 1. 密度、温度、钠原子密度、铁原子密度数据小于0的替换为空值（如numpy.nan），
    # 2. 风速小于-1000替换为空值
    print("检查比对结果是否存在异常值..")
    data_list_json = dataFilterAbnormalValue(data_list_json)

    print("预处理数据保存到数据库")
    # TODO 注意参数保存！！
    # 保存前按高度排序
    for head_id, data_list in data_list_json.items():
        # 按高度排序
        data_list_json[head_id] = sorted(data_list, key=lambda x: x['Height'])
    save_processed_data_into_db(data_list_json, conf)

    # 更新数据头信息表状态： TODO
    # updateDataHeadTable(head_id, 1, 1)      # 即：该头信息对应为预处理数据1，状态为预处理完成1
    # 更新headId
    update_processed_data_headinfo_into_db(data_list_json, conf)

    # return 0
    return {"code": 0, "msg": "数据比对完成！"}


def computeEnZValue(data_list_json, conf, TASK_ID):
    """
    计算En值、Z、zeta值等
    :param data_list_json:
    :param conf: conf['HeightStandardList']
    :param TASK_ID:
    :return:
    """
    print("计算En值和Z值..")
    # 按探测值种类-高度：计算平均探测值和平均不确定度（平均值）
    # 1.平均值（当前实现方案），2.加权算数平均值
    # 按不同的探测类型：温度、密度、风速等
    # {"valuetaype":[{"height1": {"value":[], "mcm_result":[], "gum_result_extended":[]}}, {}, ...], "valuetype2":[] }
    data_list_compare_height = {}
    for key in data_list_json.keys():
        print("[computeEnZValue]: %d" % key)
        for data in data_list_json[key]:
            # 没有GUM评定的数据跳过
            # print(data)
            if data['GumResult'] is None or data['GumResult'] == "":
                continue

            value_type = data['ValueType']
            height = data['Height']
            value = data['Value']
            gum_result = Decimal(data['GumResult'])
            gum_result_extended = Decimal(data['GumResultExtended'])

            if value_type not in data_list_compare_height.keys():
                data_list_compare_height[value_type] = {}
            if height not in data_list_compare_height[value_type].keys():
                data_list_compare_height[value_type][height] = {
                    "value": [],
                    "gum_result": [],               # 标准不确定度
                    "gum_result_extended": []       # 扩展不确定度
                }
            data_list_compare_height[value_type][height]['value'].append(value)
            data_list_compare_height[value_type][height]['gum_result'].append(gum_result)
            data_list_compare_height[value_type][height]['gum_result_extended'].append(gum_result_extended)

    # print(data_list_compare_height)

    # 计算中间分量
    data_list_compare_heigh_standard = {}
    for value_type in data_list_compare_height:
        if value_type not in data_list_compare_heigh_standard.keys():
            data_list_compare_heigh_standard[value_type] = {}
        for height in data_list_compare_height[value_type].keys():
            # 计算平均值
            value_mean = np.mean(data_list_compare_height[value_type][height]['value'])
            gum_result_mean = np.mean(data_list_compare_height[value_type][height]['gum_result'])
            gum_result_extended_mean = np.mean(data_list_compare_height[value_type][height]['gum_result_extended'])

            data_list_compare_heigh_standard[value_type][height] = {
                "x_pt": value_mean,
                "u_pt_gum": gum_result_mean,
                "u_pt_gum_extend": gum_result_extended_mean,
            }

    print("比对的En/Z比分数计算中间值：")
    print(data_list_compare_heigh_standard)

    # 计算En值、Z值、Zeta值
    for head_id in data_list_json.keys():
        for data in data_list_json[head_id]:
            # 没有GUM评定的数据跳过
            if data['GumResult'] is None or data['GumResult'] == "":
                continue

            value_type = data['ValueType']
            height = data['Height']
            x = data['Value']
            ux_gum_extend = Decimal(data['GumResultExtended'])
            ux_gum = Decimal(data['GumResult'])
            x_pt = data_list_compare_heigh_standard[value_type][height]["x_pt"]
            u_pt_gum_extend = data_list_compare_heigh_standard[value_type][height]["u_pt_gum_extend"]
            u_pt_gum = data_list_compare_heigh_standard[value_type][height]["u_pt_gum"]

            # 3个值可设置固定范围：（-5,5）（界面画图用！）
            data['EnResult'] = (x - x_pt) / math.sqrt(ux_gum_extend ** 2 + u_pt_gum_extend ** 2)
            data['ZResult'] = Decimal.from_float(x - x_pt) / ux_gum
            data['ZetaResult'] = (x - x_pt) / math.sqrt(ux_gum ** 2 + u_pt_gum ** 2)

            print("En/Z/Zeta：")
            print(data['EnResult'], data['ZResult'], data['ZetaResult'])

    return data_list_json


def computeEnZValue_bak(data_list_json, conf, TASK_ID):
    """
    计算En值、Z、zeta值等（进行系统联调测试用，不作为最后正式计算）
    :param data_list_json:
    :param conf: conf['HeightStandardList']
    :param TASK_ID:
    :return:
    """
    # 测试（产生随机数，仅用于测试）
    max = math.floor(12.2) * 0.8

    # 计算En值、Z值、Zeta值
    for head_id in data_list_json.keys():
        for data in data_list_json[head_id]:
            data['EnResult'] = obj_to_strV2(random.uniform(0, max))
            data['ZResult'] = obj_to_strV2(random.uniform(0, max))
            data['ZetaResult'] = obj_to_strV2(random.uniform(0, max))

    return data_list_json


def compareDataWithStandard(data_list_json, standard_probe_data):
    """
    同标准廓线比对
    :param data_list_json:
    :param standard_probe_data:
    :return:
    """
    for key in data_list_json.keys():
        # print(data_list_json[key])
        for index in range(len(data_list_json[key])):
            data = data_list_json[key][index]
            # if data['Value'] - NoneValue <= EPSINON:
            if 'U20' in data.keys() and data['U20'] == NoneStr:  # 识别出填充的高度值
                continue
            height = data['Height']
            h_k = '%.2f' % height
            if h_k in standard_probe_data.keys():
                standard_data = standard_probe_data[h_k]
                #print(standard_data)
                # 测量数据类型:0温度1密度2径向风速3纬向风速
                if data['ValueType'] == 0:
                    standard_value = standard_data['TemperatureProfile']
                elif data['ValueType'] == 1:
                    standard_value = standard_data['TemperatureProfile']
                elif data['ValueType'] == 2 or data['ValueType'] == 3:
                    standard_value = standard_data['WindSpeedProfile']
                else:
                    standard_value = 0.0

                # 标准廓线的数据存放在U1字段，差值存放在U2字段
                data_list_json[key][index]['U1'] = standard_value
                data_list_json[key][index]['U2'] = "%.6f" % (Decimal(data['Value']) - Decimal(standard_value))

                # 测试
                #print(data_list_json[key])

    return data_list_json


def compareDataWithAssignedStandardData(data_list_json, data_head_id_list_group, conf):
    """
    同指定的廓线比对：不同设备，相同时间
    :param data_list_json:
    :param data_head_id_list_group:
    :return:
    """
    for key in data_head_id_list_group.keys():
        head_id_list = data_head_id_list_group[key]
        if len(head_id_list) > 0:
            # 以第1个head_id作为标准数据进行比对
            standard_data_head_id = head_id_list[0]
            standard_data = data_list_json[standard_data_head_id]
            # 重新组织标准数据，便于比对中获取相应高度的探测值
            standard_data_new = {}
            for index in range(len(standard_data)):
                height = standard_data[index]['Height']
                standard_data_new[height] = standard_data[index]
        # print(standard_data_new)

        if conf['IsTimeProcessed'] == 0:
            en_z_value_res = getEnZValue(head_id_list, data_list_json)

        # 所有head_id的数据都和第1个head_id做差值，包括第1个head_id和他自己做差值
        for head_id in head_id_list:
            # print("#####: %d" % head_id)
            if head_id not in data_list_json.keys():
                continue

            for index in range(len(data_list_json[head_id])):
                data = data_list_json[head_id][index]
                height = data['Height']
                if height in standard_data_new.keys():
                    standard_value = standard_data_new[height]['Value']

                    # 标准数据存放在U1字段，差值存放在U2字段，标识存放U3字段（即每个站点和设备都有同一时间区间的数据）
                    data_list_json[head_id][index]['U1'] = standard_value
                    data_list_json[head_id][index]['U2'] = "%.6f" % (Decimal(data['Value']) - Decimal(standard_value))
                    # data_list_json[head_id][index]['U3'] = 1  # TODO

                    if conf['IsTimeProcessed'] == 0:
                        data_list_json[head_id][index]['EnResult'] = en_z_value_res[head_id][index]['EnResult']
                        data_list_json[head_id][index]['ZResult'] = en_z_value_res[head_id][index]['ZResult']
                        data_list_json[head_id][index]['ZetaResult'] = en_z_value_res[head_id][index]['ZetaResult']
                    else:
                        data_list_json[head_id][index]['EnResult'] = ""
                        data_list_json[head_id][index]['ZResult'] = ""
                        data_list_json[head_id][index]['ZetaResult'] = ""

    return data_list_json


# 数据比对--“观测数据-观测数据比对”子模块增加功能：添加“评价指标计算”按钮，结果生成三张图：平均偏差、标准偏差、相关系数。
def drawIndexCalculationFigure(data_list_json, data_head_id_group_by_station_device_new, conf):
    print("评价指标计算..")
    # print(data_list_json)
    # print(data_list_json.keys())
    # 转成dataframe，调用303所提供的画图接口
    print("===========================11111==============================")
    probe_data_obj = {}
    # 类似二维彩图、时空分析图，所有同参量类型数据放在一个list（不符合要求）
    # for head_id, data_list in data_list_json.items():
    #     # 按高度排序
    #     data_list = sorted(data_list, key=lambda x: x['Height'])
    #
    #     row_data_list = []
    #     if len(data_list) > 0:
    #         value_type = data_list[0]['ValueType']
    #         data_type = checkL2DataTypeByValueType2309(value_type)
    #
    #     for obj in data_list:
    #         # DataFrame中的行数据
    #         row_data = [obj['Height'], float(obj['Value']), obj['BinKey']]
    #         row_data_list.append(row_data)
    #
    #     # 按高度排序
    #     # print("数组：", row_data_list)
    #     df = pd.DataFrame(row_data_list, columns=['Height', 'Value', 'DataStartTime'])
    #     # print(df)
    #
    #     # print(head_id, data_type)
    #     # print(probe_data_obj.keys())
    #     if data_type not in probe_data_obj.keys():
    #         probe_data_obj[data_type] = []
    #     probe_data_obj[data_type].append(df)

    # 新逻辑：按组存放，不同设备的数据存放在不同的dataframe中，然后放在同类参量类型中
    # 如： {'T': [dataframe1, dataframe2, dataframe3, ...], 'D': []}
    for station_device_pair, head_id_list in data_head_id_group_by_station_device_new.items():
        row_data_list = []
        for head_id in head_id_list:
            print("获取廓线: ", head_id)
            data_list = data_list_json[head_id]
            # 按高度排序
            data_list = sorted(data_list, key=lambda x: x['Height'])

            if len(data_list) > 0:
                value_type = data_list[0]['ValueType']
                data_type = checkL2DataTypeByValueType2309(value_type)

            for obj in data_list:
                # DataFrame中的行数据
                row_data = [obj['Height'], float(obj['Value']), obj['BinKey']]
                row_data_list.append(row_data)

        # 同一站点-设备的所有廓线存入同一个dataframe
        # print("数组：", row_data_list)
        print("生成DataFrame...")
        df = pd.DataFrame(row_data_list, columns=['Height', 'Value', 'DataStartTime'])
        # print(df)

        # print(head_id, data_type)
        # print(probe_data_obj.keys())
        if data_type not in probe_data_obj.keys():
            probe_data_obj[data_type] = []
        probe_data_obj[data_type].append(df)

    ###############################################################
    # TODO 调用接口画“评价指标计算”图：平均偏差、标准偏差、相关系数。
    # 结果图保存路径
    save_path = conf['FigureSavePath']
    if not os.path.exists(save_path):
        try:
            os.makedirs(save_path)
        except Exception as e:
            print("创建路径报错：", e)
    else:
        # 清空文件夹数据
        clear_folder_shutil(save_path)

    # 检查画图廓线数据参数合法性
    if len(probe_data_obj) == 0:
        print("无符合评价指标计算结果画图的廓线数据！")
        return {"code": -1, "msg": "无符合评价指标计算结果画图的廓线数据！"}
        # return -1

    print("画“评价指标计算”图参数：", probe_data_obj)
    figure_conf = {
        "figure_location": save_path,     # 结果图保存路径
        "xx": "",                         # 可扩展其他参数
    }

    # TODO 替换为新接口
    # 返回格式：
    # obj = {'T':{"MD":"/figure/path/figure1_md.png", "SD":"/figure/path/figure1_md.png", "COEF":"/figure/path/figure1_md.png"},
    #        'D':{"MD":"/figure/path/figure2_md.png", "SD":"/figure/path/figure2_md.png", "COEF":"/figure/path/figure2_md.png"}}
    # result = drawIndexFigureMock(probe_data_obj, figure_conf)
    result = dataCompareDrawIndexFigure(probe_data_obj, figure_conf)
    print("评价指标计算画图结束，返回：", result)

    for key, value in result.items():
        for subkey, subvalue in  value.items():
            fig_type, fig_name = getFigureNameByKey(subkey)
            param = {
                "BatchId": conf['TaskId'],
                "TaskType": 1,                  # 任务类型：0评定；1数据比对；2数据分析
                "FigureType": fig_type,         # 图片类型，适用于数据比对（0平均偏差、1标准偏差、2相关系数）
                "FigureName": fig_name,
                "DataType": key,
                "ValueType": convertDataType2ValueType(key),
                "FigureLocation": subvalue.replace("\\", "/"),
                "Remark": "两组观测数据比对-评价指标计算"
            }
            ret_id = saveDrawFigureResultIntoDB(param)

    print("===========================22222==============================")
    # return 0
    return {"code": 0, "msg": "评价指标计算完成！"}


def getFigureNameByKey(key):
    fig_name = (-1, "")
    if key == "MD":
        fig_name = (0, "平均偏差")
    elif key == "SD":
        fig_name = (1, "标准偏差")
    elif key == "COEF":
        fig_name = (2, "相关系数")
    return fig_name


# 返回值需要包括：参量类型，图片的全路径（“评价指标计算”图：平均偏差MD、标准偏差SD、相关系数COEF），例如：
# ｛'T': "/figure/path/figure1.png", 'D': "/figure/path/figure2.png",  ...｝
def drawIndexFigureMock(data_obj, figure_conf):
    obj = {'T':{"MD":"/figure/path/figure1_md.png", "SD":"/figure/path/figure1_md.png", "COEF":"/figure/path/figure1_md.png"},
           'D':{"MD":"/figure/path/figure2_md.png", "SD":"/figure/path/figure2_md.png", "COEF":"/figure/path/figure2_md.png"}}
    return obj


def compareDataWithAssignedStandardDataSameDeviceDiffTime(data_list_json):
    """
    同指定的廓线比对：相同设备，不同时间
    :param data_list_json:
    :param data_head_id_list_group:
    :return:
    """
    # 取第一个数据文件作为比对标准
    for head_id in data_list_json.keys():
        standard_data = data_list_json[head_id]
        # 重新组织标准数据，便于比对中获取相应高度的探测值
        standard_data_new = {}
        for index in range(len(standard_data)):
            height = standard_data[index]['Height']
            standard_data_new[height] = standard_data[index]
        break

    for head_id in data_list_json.keys():
        # print(data_list_json[key])
        for index in range(len(data_list_json[head_id])):
            data = data_list_json[head_id][index]
            height = data['Height']

            if height in standard_data_new.keys():
                standard_value = standard_data_new[height]['Value']

                # 标准数据存放在U1字段，差值存放在U2字段，标识存放U3字段（即每个站点和设备都有同一时间区间的数据）
                data_list_json[head_id][index]['U1'] = standard_value
                data_list_json[head_id][index]['U2'] = "%.6f" % (Decimal(data['Value']) - Decimal(standard_value))
                # data_list_json[head_id][index]['U3'] = 1  # TODO
    return data_list_json


def getEnZValue(head_id_list, data_list_json):
    """
    对head_id_list中的几个文件计算En、Z、Zeta值
    :param head_id_list:
    :param data_list_json:
    :return:
    """
    data_list_for_compute = {}
    for head_id in head_id_list:
        if head_id in data_list_json.keys():
            data_list_for_compute[head_id] = data_list_json[head_id]

    return computeEnZValueV2(data_list_for_compute)


def computeEnZValueV2(data_list_json):
    """
    计算En值、Z、zeta值等
    :param data_list_json
    :return:
    """
    data_list_compare_height = {}
    for key in data_list_json.keys():
        print("[computeEnZValue]: %d" % key)
        for data in data_list_json[key]:
            # 没有GUM评定的数据跳过
            # print(data)
            if data['GumResult'] is None or data['GumResult'] == "":
                continue

            value_type = data['ValueType']
            height = data['Height']
            value = data['Value']
            gum_result = Decimal(data['GumResult'])
            gum_result_extended = Decimal(data['GumResultExtended'])

            if value_type not in data_list_compare_height.keys():
                data_list_compare_height[value_type] = {}
            if height not in data_list_compare_height[value_type].keys():
                data_list_compare_height[value_type][height] = {
                    "value": [],
                    "gum_result": [],               # 标准不确定度
                    "gum_result_extended": []       # 扩展不确定度
                }
            data_list_compare_height[value_type][height]['value'].append(value)
            data_list_compare_height[value_type][height]['gum_result'].append(gum_result)
            data_list_compare_height[value_type][height]['gum_result_extended'].append(gum_result_extended)

    # print(data_list_compare_height)

    # 计算中间分量
    data_list_compare_heigh_standard = {}
    for value_type in data_list_compare_height:
        if value_type not in data_list_compare_heigh_standard.keys():
            data_list_compare_heigh_standard[value_type] = {}
        for height in data_list_compare_height[value_type].keys():
            # 计算平均值
            value_mean = np.mean(data_list_compare_height[value_type][height]['value'])
            gum_result_mean = np.mean(data_list_compare_height[value_type][height]['gum_result'])
            gum_result_extended_mean = np.mean(data_list_compare_height[value_type][height]['gum_result_extended'])

            data_list_compare_heigh_standard[value_type][height] = {
                "x_pt": value_mean,
                "u_pt_gum": gum_result_mean,
                "u_pt_gum_extend": gum_result_extended_mean,
            }

    print("比对的En/Z比分数计算中间值：")
    print(data_list_compare_heigh_standard)

    # 计算En值、Z值、Zeta值
    for head_id in data_list_json.keys():
        for data in data_list_json[head_id]:
            # 没有GUM评定的数据跳过
            if data['GumResult'] is None or data['GumResult'] == "":
                continue

            value_type = data['ValueType']
            height = data['Height']
            x = data['Value']
            ux_gum_extend = Decimal(data['GumResultExtended'])
            ux_gum = Decimal(data['GumResult'])
            x_pt = data_list_compare_heigh_standard[value_type][height]["x_pt"]
            u_pt_gum_extend = data_list_compare_heigh_standard[value_type][height]["u_pt_gum_extend"]
            u_pt_gum = data_list_compare_heigh_standard[value_type][height]["u_pt_gum"]

            # 3个值可设置固定范围：（-5,5）（界面画图用！）
            data['EnResult'] = (x - x_pt) / math.sqrt(ux_gum_extend ** 2 + u_pt_gum_extend ** 2)
            data['ZResult'] = Decimal.from_float(x - x_pt) / ux_gum
            data['ZetaResult'] = (x - x_pt) / math.sqrt(ux_gum ** 2 + u_pt_gum ** 2)

            print("En/Z/Zeta：")
            print(data['EnResult'], data['ZResult'], data['ZetaResult'])

    return data_list_json
